package com.zxy.ziems.server.board.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zxy.btp.redis.utils.RedisUtil;
import com.zxy.ziems.server.board.pojo.vo.ClassifyDosageVo;
import com.zxy.ziems.server.board.pojo.vo.DosageVo;
import com.zxy.ziems.server.board.pojo.vo.EnergyTrendVo;
import com.zxy.ziems.server.board.pojo.vo.EnergyTop5Vo;
import com.zxy.ziems.server.board.service.EnergyBoardService;
import com.zxy.ziems.server.classify.pojo.entity.Classify;
import com.zxy.ziems.server.classify.pojo.entity.ClassifyDeviceProperty;
import com.zxy.ziems.server.classify.service.ClassifyDevicePropertyService;
import com.zxy.ziems.server.classify.service.ClassifyService;
import com.zxy.ziems.server.constant.enums.DictCodeEnum;
import com.zxy.ziems.server.device.service.DataUnitService;
import com.zxy.ziems.server.domain.DictItem;
import com.zxy.ziems.server.energy.mapper.DeviceMeasureDataDayMapper;
import com.zxy.ziems.server.energy.pojo.entity.DeviceMeasureDataDayEntity;
import com.zxy.ziems.server.service.DictItemService;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.BusinessExceptionUtil;
import com.zxy.ziems.server.utils.DateUtils;
import com.zxy.ziems.server.vo.ChartVo;
import com.zxy.ziems.server.vo.ChartYAxisVo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zxy.ziems.server.constant.RedisKeyConstant.*;
import static com.zxy.ziems.server.utils.DateUtils.MD_PATTERN;
import static com.zxy.ziems.server.utils.DateUtils.YYM_PATTERN;

@Service
public class EnergyBoardServiceImpl implements EnergyBoardService {

    @Autowired
    private DictItemService dictItemService;
    @Autowired
    private ClassifyDevicePropertyService classifyDevicePropertyService;
    @Autowired
    private DeviceMeasureDataDayMapper deviceMeasureDataDayMapper;
    @Autowired
    private ClassifyService classifyService;
    @Autowired
    private DataUnitService dataUnitService;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public EnergyTop5Vo getEnergyTop5() {

        List<DictItem> dictItems = getDictItemsTop5();

        EnergyTop5Vo res = new EnergyTop5Vo();
        LocalDate yesterday = LocalDate.now().plusDays(-1);

        // 缓存key，昨日数据，缓存两天
        String key = ENERGY_BOARD_TOP_5_DAY_CACHE_KEY + yesterday.toString();
        EnergyTop5Vo cacheData = Optional.ofNullable(redisUtil.get(key)).map(o -> (EnergyTop5Vo) o).orElse(null);
        if (Objects.nonNull(cacheData) && CollectionUtil.isNotEmpty(cacheData.getEnergyTop5())) {
            return cacheData;
        }

        Map<String, List<DosageVo>> energyTop5 = new HashMap<>();
        dictItems.stream().forEach(dictItem -> {
            List<String> classifyIds = classifyService.getClassifyId(dictItem.getItemCode());
            List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
            AssertUtil.nonNull(classifyDeviceProperties, "未配置分类");
            if (CollectionUtil.isEmpty(classifyDeviceProperties)) {
                return;
            }
            Integer limit = dictItem.getItemName().equals("电") ? 5 : 3;
            List<DosageVo> top5 = deviceMeasureDataDayMapper.top5Sum(dictItem.getItemCode(), classifyDeviceProperties.get(0).getDataTypeId(), yesterday, limit);
            top5.stream().forEach(m -> m.setValue(m.getValue().setScale(0, RoundingMode.HALF_UP)));
            if (CollectionUtil.isEmpty(top5)) {
                return;
            }
            energyTop5.put(dictItem.getItemName(), top5);
        });


        res.setEnergyTop5(energyTop5);
        redisUtil.set(key, res, 5L, TimeUnit.MINUTES);
        return res;
    }

    private List<DictItem> getDictItemsTop5() {
        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENERGY_BOARD.getCode());
        if (CollectionUtil.isEmpty(dictItems)) {
            throw BusinessExceptionUtil.exception("未配置字典参数");
        }
        return dictItems;
    }

    @Override
    public ClassifyDosageVo getCurMonthBoard() {

        ClassifyDosageVo res = new ClassifyDosageVo();

        List<DictItem> dictItems = getDictItems();

        LocalDate startDate = DateUtils.getMonthStartDate(LocalDateTime.now(), 0).toLocalDate();
        LocalDate endDate = DateUtils.getMonthEndDate(LocalDateTime.now(), 0).toLocalDate();

        String key = ENERGY_BOARD_MONTH_CLASSIFY_CACHE_KEY + startDate.toString();
        Object o = redisUtil.get(key);
        if (o != null) {
            res = (ClassifyDosageVo) o;
            return res;
        }

        Map<String, DosageVo> monthClassify = new HashMap<>();
        dictItems.stream().forEach(dictItem -> {
            DosageVo vo = new DosageVo();
            Classify classify = classifyService.getById(dictItem.getItemCode());
            vo.setName(classify.getClassifyName());
            vo.setUnit(dataUnitService.getById(classify.getUnitId()).getName());
            List<String> classifyIds = classifyService.getClassifyId(dictItem.getItemCode());
            List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
            AssertUtil.nonNull(classifyDeviceProperties, "未配置分类");
            if (CollectionUtil.isEmpty(classifyDeviceProperties)) {
                return;
            }
            BigDecimal val = deviceMeasureDataDayMapper.classifySum(dictItem.getItemCode(), classifyDeviceProperties.get(0).getDataTypeId(), startDate, endDate);
            vo.setValue(Optional.ofNullable(val).orElse(BigDecimal.ZERO).setScale(0, RoundingMode.HALF_UP));
            monthClassify.put(dictItem.getItemName(), vo);
        });


        res.setClassifyDosage(monthClassify);
        redisUtil.set(key, res, 5L, TimeUnit.MINUTES);
        return res;
    }

    @Override
    public EnergyTrendVo getSevenTrend() {
        EnergyTrendVo res = new EnergyTrendVo();

        List<LocalDate> dateList = DateUtils.getLastDateList(7);
        String key = ENERGY_BOARD_SEVEN_TREND_CACHE_KEY + dateList.get(0).toString();
        Object o = redisUtil.get(key);
        if (o != null) {
            res = (EnergyTrendVo) o;
            return res;
        }

        List<DictItem> dictItems = getDictItems();

        Map<String, List<ChartVo>> energySevenTrend = new HashMap<>();
        res.setEnergyTrend(energySevenTrend);
        // 非电数，电与其他不同
        dictItems.stream().forEach(dictItem -> {
            List<ChartVo> list = null;
            LocalDate startDate = dateList.get(0);
            LocalDate endDate = dateList.get(dateList.size() - 1);
            if (!dictItem.getItemName().equals("电")) {
                list = getChartVoList(dateList, dictItem, MD_PATTERN, "%m-%d", startDate, endDate);
            } else {
                List<Classify> childrenClassify = classifyService.getChildrenClassify(dictItem.getItemCode());
                if (CollectionUtil.isEmpty(childrenClassify)) {
                    return;
                }
                String dataTypeId = getDataTypeId(dictItem);
                list = new ArrayList<>();


                List<DosageVo> dosageVos = deviceMeasureDataDayMapper.sumByDateTypeAndClassifyIds(childrenClassify.stream().map(Classify::getClassifyId).collect(Collectors.toList()),
                        dataTypeId, startDate, endDate, "%m-%d");
                if (CollectionUtil.isEmpty(dosageVos)) {
                    return;
                }
                Map<String, List<DosageVo>> classifyMap = dosageVos.stream().collect(Collectors.groupingBy(DosageVo::getClassifyName));
                ChartVo chartVo = new ChartVo();
                List<String> xAxisList = DateUtils.dateToString(dateList, MD_PATTERN);
                chartVo.setXAxisList(xAxisList);
                List<ChartYAxisVo> yAxisList = new ArrayList<>();
                for (Map.Entry<String, List<DosageVo>> entry : classifyMap.entrySet()) {
                    ChartYAxisVo chartYAxisVo = new ChartYAxisVo();
                    yAxisList.add(chartYAxisVo);
                    chartYAxisVo.setName(entry.getKey());

                    Map<String, BigDecimal> dataMap = entry.getValue().stream().collect(Collectors.toMap(DosageVo::getName, DosageVo::getValue));
                    // 根据x轴设置值
                    chartYAxisVo.setData(xAxisList.stream().map(m -> {
                        BigDecimal decimal = dataMap.get(m);
                        if (decimal == null) {
                            decimal = BigDecimal.ZERO;
                        }
                        return decimal;

                    }).collect(Collectors.toList()));
                }
                chartVo.setYAxisList(yAxisList);
                list.add(chartVo);

            }
            if (list == null) return;

            energySevenTrend.put(dictItem.getItemName(), list);
        });

        redisUtil.set(key, res, 5L, TimeUnit.MINUTES);
        return res;
    }

    @Nullable
    private List<ChartVo> getChartVoList(List<LocalDate> dateList, DictItem dictItem, String dateFormat, String sqlDateFormat, LocalDate startDate, LocalDate endDate) {
        String dataTypeId = getDataTypeId(dictItem);
        List<ChartVo> list = new ArrayList<>();
        ChartVo chartVo = new ChartVo();
        List<String> xAxisList = DateUtils.dateToString(dateList, dateFormat);
        chartVo.setXAxisList(xAxisList);
        list.add(chartVo);
        List<DosageVo> dosageVos = deviceMeasureDataDayMapper.sumByDateType(dictItem.getItemCode(), dataTypeId,
                startDate, endDate, sqlDateFormat);
        Map<String, BigDecimal> dataMap = dosageVos.stream().collect(Collectors.toMap(DosageVo::getName, DosageVo::getValue));

        List<ChartYAxisVo> yAxisList = new ArrayList<>();
        ChartYAxisVo chartYAxisVo = new ChartYAxisVo();
        yAxisList.add(chartYAxisVo);
        chartYAxisVo.setName(dictItem.getItemName());
        // 根据x轴设置值
        chartYAxisVo.setData(xAxisList.stream().map(m -> {
            BigDecimal decimal = dataMap.get(m);
            if (decimal == null) {
                decimal = BigDecimal.ZERO;
            }
            return decimal;

        }).collect(Collectors.toList()));
        chartVo.setYAxisList(yAxisList);
        return list;
    }

    @Nullable
    private String getDataTypeId(DictItem dictItem) {
        List<String> classifyIds = classifyService.getClassifyId(dictItem.getItemCode());
        List<ClassifyDeviceProperty> classifyDeviceProperties = classifyDevicePropertyService.getByClassIds(classifyIds);
        if (CollectionUtil.isEmpty(classifyDeviceProperties)) {
            throw BusinessExceptionUtil.exception("未配置分类");
        }
        return classifyDeviceProperties.get(0).getDataTypeId();
    }

    @Override
    public EnergyTrendVo getMonthTrend() {
        EnergyTrendVo res = new EnergyTrendVo();

        // 获取最早数据
        QueryWrapper<DeviceMeasureDataDayEntity> param = new QueryWrapper<>();
        param.lambda().orderByAsc(DeviceMeasureDataDayEntity::getId);
        Page<DeviceMeasureDataDayEntity> page = new Page<>(1, 1);
        Page<DeviceMeasureDataDayEntity> pageRes = deviceMeasureDataDayMapper.selectPage(page, param);
        if (pageRes.getTotal() < 1) {
            throw BusinessExceptionUtil.exception("无数据");
        }
        DeviceMeasureDataDayEntity deviceMeasureDataDayEntity = pageRes.getRecords().get(0);
        LocalDate measureTime = deviceMeasureDataDayEntity.getMeasureTime();
        List<LocalDate> dateList = DateUtils.getMonths(DateUtils.getMonthStartDate(measureTime.atStartOfDay(), 0).toLocalDate());

        String key = ENERGY_BOARD_MONTH_TREND_CACHE_KEY + dateList.get(0).toString();
        Object o = redisUtil.get(key);
        if (o != null) {
            res = (EnergyTrendVo) o;
            return res;
        }

        List<DictItem> dictItems = getDictItems();

        Map<String, List<ChartVo>> energySevenTrend = new HashMap<>();
        res.setEnergyTrend(energySevenTrend);
        // 非电数，电与其他不同
        dictItems.parallelStream().forEach(dictItem -> {
            List<ChartVo> list = getChartVoList(dateList, dictItem, YYM_PATTERN, "%y-%m", dateList.get(0), LocalDate.now().plusDays(-1));
            if (list == null) return;

            energySevenTrend.put(dictItem.getItemName(), list);
        });
        redisUtil.set(key, res, 5L, TimeUnit.MINUTES);
        return res;
    }

    @NotNull
    private List<DictItem> getDictItems() {
        List<DictItem> dictItems = dictItemService.getByDictCode(DictCodeEnum.ENERGY_BOARD_ALL.getCode());
        if (CollectionUtil.isEmpty(dictItems)) {
            throw BusinessExceptionUtil.exception("未配置字典参数");
        }
        return dictItems;
    }
}
